home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1993 July / InfoMagic USENET CD-ROM July 1993.ISO / sources / unix / volume18 / mush6.4 / part09 < prev    next >
Encoding:
Internet Message Format  |  1989-03-12  |  41.4 KB

  1. Subject:  v18i031:  Mail user's shell version 6.4, Part09/19
  2. Newsgroups: comp.sources.unix
  3. Sender: sources
  4. Approved: rsalz@uunet.UU.NET
  5.  
  6. Submitted-by: Dan Heller <island!argv@sun.com>
  7. Posting-number: Volume 18, Issue 31
  8. Archive-name: mush6.4/part09
  9.  
  10.  
  11.  
  12. #! /bin/sh
  13. # This is a shell archive.  Remove anything before this line, then unpack
  14. # it by saving it into a file and typing "sh file".  To overwrite existing
  15. # files, type "sh file -c".  You can also feed this as standard input via
  16. # unshar, or by typing "sh <file", e.g..  If this archive is complete, you
  17. # will see the following message at the end:
  18. #        "End of archive 9 (of 19)."
  19. # Contents:  bind.c hdrs.c
  20. # Wrapped by rsalz@papaya.bbn.com on Mon Mar 13 19:25:15 1989
  21. PATH=/bin:/usr/bin:/usr/ucb ; export PATH
  22. if test -f 'bind.c' -a "${1}" != "-c" ; then 
  23.   echo shar: Will not clobber existing file \"'bind.c'\"
  24. else
  25. echo shar: Extracting \"'bind.c'\" \(20444 characters\)
  26. sed "s/^X//" >'bind.c' <<'END_OF_FILE'
  27. X/* bind.c */
  28. X
  29. X#include "bindings.h"
  30. X#include "mush.h"
  31. X
  32. Xextern char *c_macro();
  33. Xstatic un_bind();
  34. X
  35. Xstruct cmd_map *cmd_map, *line_map, *bang_map;
  36. X
  37. X/*
  38. X * Bindings are added here in REVERSE of the order that
  39. X * they will be displayed!  Display order is based on a
  40. X * guess about the frequency of use and (to a lesser
  41. X * extent) how hard they are to remember.
  42. X *
  43. X * The user's own new bindings, if any, will be displayed
  44. X * before any of these default bindings.
  45. X */
  46. Xinit_bindings()
  47. X{
  48. X#ifdef CURSES
  49. X    /* Help gets displayed last */
  50. X    add_bind("?", C_HELP, NULL, &cmd_map);
  51. X    add_bind("V", C_VERSION, NULL, &cmd_map);
  52. X
  53. X    /* Miscellaneous shell commands */
  54. X    add_bind("%", C_CHDIR, NULL, &cmd_map);
  55. X    add_bind("|", C_PRINT_MSG, NULL, &cmd_map);
  56. X    add_bind("!", C_SHELL_ESC, NULL, &cmd_map);
  57. X    add_bind(":", C_CURSES_ESC, NULL, &cmd_map);
  58. X
  59. X    /* Mush customization commands */
  60. X    /* NOTE: No default C_MACRO bindings */
  61. X    add_bind(")", C_SAVEOPTS, NULL, &cmd_map);
  62. X    add_bind("(", C_SOURCE, NULL, &cmd_map);
  63. X    add_bind("&!", C_MAP_BANG, NULL, &cmd_map);
  64. X    add_bind("&:", C_MAP, NULL, &cmd_map);
  65. X    add_bind("&&", C_BIND_MACRO, NULL, &cmd_map);
  66. X    add_bind("v", C_VAR_SET, NULL, &cmd_map);
  67. X    add_bind("i", C_IGNORE, NULL, &cmd_map);
  68. X    add_bind("h", C_OWN_HDR, NULL, &cmd_map);
  69. X    add_bind("B", C_UNBIND, NULL, &cmd_map);
  70. X    add_bind("b", C_BIND, NULL, &cmd_map);
  71. X    add_bind("a", C_ALIAS, NULL, &cmd_map);
  72. X
  73. X    /* Display modification commands */
  74. X    add_bind("\022", C_REVERSE, NULL, &cmd_map);    /* ^R */
  75. X    add_bind("\014", C_REDRAW, NULL, &cmd_map);        /* ^L */
  76. X    add_bind("Z", C_PREV_SCREEN, NULL, &cmd_map);
  77. X    add_bind("z", C_NEXT_SCREEN, NULL, &cmd_map);
  78. X
  79. X    /* Searching and sorting commands */
  80. X    add_bind("\016", C_CONT_SEARCH, NULL, &cmd_map);    /* ^N */
  81. X    add_bind("\037", C_PREV_SEARCH, NULL, &cmd_map);    /* ^/ */
  82. X    add_bind("/", C_NEXT_SEARCH, NULL, &cmd_map);
  83. X    add_bind("O", C_REV_SORT, NULL, &cmd_map);
  84. X    add_bind("o", C_SORT, NULL, &cmd_map);
  85. X
  86. X    /* Ways to get out */
  87. X    add_bind("X", C_EXIT_HARD, NULL, &cmd_map);
  88. X    add_bind("x", C_EXIT, NULL, &cmd_map);
  89. X    add_bind("Q", C_QUIT_HARD, NULL, &cmd_map);
  90. X    add_bind("q", C_QUIT, NULL, &cmd_map);
  91. X
  92. X    /* Folder modification commands */
  93. X    add_bind("\025", C_UPDATE, NULL, &cmd_map);        /* ^U */
  94. X    add_bind("\020", C_PRESERVE, NULL, &cmd_map);    /* ^P */
  95. X    add_bind("W", C_WRITE_LIST, NULL, &cmd_map);
  96. X    add_bind("w", C_WRITE_MSG, NULL, &cmd_map);
  97. X    add_bind("U", C_UNDEL_LIST, NULL, &cmd_map);
  98. X    add_bind("u", C_UNDEL_MSG, NULL, &cmd_map);
  99. X    add_bind("S", C_SAVE_LIST, NULL, &cmd_map);
  100. X    add_bind("s", C_SAVE_MSG, NULL, &cmd_map);
  101. X    add_bind("f", C_FOLDER, NULL, &cmd_map);
  102. X    add_bind("D", C_DELETE_LIST, NULL, &cmd_map);
  103. X    add_bind("d", C_DELETE_MSG, NULL, &cmd_map);
  104. X    add_bind("C", C_COPY_LIST, NULL, &cmd_map);
  105. X    add_bind("c", C_COPY_MSG, NULL, &cmd_map);
  106. X
  107. X    /* Cursor movement and message selection */
  108. X    add_bind("g", C_GOTO_MSG, NULL, &cmd_map);
  109. X    add_bind("}", C_BOTTOM_PAGE, NULL, &cmd_map);
  110. X    add_bind("{", C_TOP_PAGE, NULL, &cmd_map);
  111. X    add_bind("$", C_LAST_MSG, NULL, &cmd_map);
  112. X    add_bind("^", C_FIRST_MSG, NULL, &cmd_map);
  113. X    add_bind("\013",C_PREV_MSG, NULL, &cmd_map);    /* ^K */
  114. X    add_bind("\012", C_NEXT_MSG, NULL, &cmd_map);    /* ^J */
  115. X    add_bind("-",C_PREV_MSG, NULL, &cmd_map);
  116. X    add_bind("+",C_NEXT_MSG, NULL, &cmd_map);
  117. X    add_bind("K", C_PREV_MSG, NULL, &cmd_map);
  118. X    add_bind("k", C_PREV_MSG, NULL, &cmd_map);
  119. X    add_bind("J", C_NEXT_MSG, NULL, &cmd_map);
  120. X    add_bind("j", C_NEXT_MSG, NULL, &cmd_map);
  121. X
  122. X    /* Mail-sending commands */
  123. X    add_bind("R", C_REPLY_ALL, NULL, &cmd_map);
  124. X    add_bind("r", C_REPLY_SENDER, NULL, &cmd_map);
  125. X    add_bind("M", C_MAIL_FLAGS, NULL, &cmd_map);
  126. X    add_bind("m", C_MAIL, NULL, &cmd_map);
  127. X
  128. X    /* Mail-reading commands */
  129. X    add_bind(".", C_DISPLAY_MSG, NULL, &cmd_map);
  130. X    add_bind("T", C_TOP_MSG, NULL, &cmd_map);
  131. X    add_bind("t", C_DISPLAY_MSG, NULL, &cmd_map);
  132. X    add_bind("p", C_DISPLAY_MSG, NULL, &cmd_map);
  133. X    add_bind("n", C_DISPLAY_NEXT, NULL, &cmd_map);
  134. X
  135. X#endif /* CURSES */
  136. X}
  137. X
  138. X/* Bindable function names.
  139. X *  Most of these can't be used if CURSES is not defined,
  140. X *  but help and lookups get confused if they aren't all here.
  141. X */
  142. Xstruct cmd_map map_func_names[] = {
  143. X    /* These MUST be in numerical order; see bindings.h */
  144. X    { C_NULL,        "no-op",        NULL, NULL_MAP },
  145. X    { C_GOTO_MSG,    "goto-msg",        NULL, NULL_MAP },
  146. X    { C_WRITE_LIST,    "write-list",        NULL, NULL_MAP },
  147. X    { C_WRITE_MSG,    "write",        NULL, NULL_MAP },
  148. X    { C_SAVE_LIST,    "save-list",        NULL, NULL_MAP },
  149. X    { C_SAVE_MSG,    "save",            NULL, NULL_MAP },
  150. X    { C_COPY_LIST,    "copy-list",        NULL, NULL_MAP },
  151. X    { C_COPY_MSG,    "copy",            NULL, NULL_MAP },
  152. X    { C_DELETE_LIST,    "delete-list",        NULL, NULL_MAP },
  153. X    { C_DELETE_MSG,    "delete",        NULL, NULL_MAP },
  154. X    { C_UNDEL_LIST,    "undelete-list",    NULL, NULL_MAP },
  155. X    { C_UNDEL_MSG,    "undelete",        NULL, NULL_MAP },
  156. X    { C_REDRAW,        "redraw",        NULL, NULL_MAP },
  157. X    { C_REVERSE,    "reverse-video",    NULL, NULL_MAP },
  158. X    { C_NEXT_MSG,    "next-msg",        NULL, NULL_MAP },
  159. X    { C_PREV_MSG,    "back-msg",        NULL, NULL_MAP },
  160. X    { C_FIRST_MSG,    "first-msg",        NULL, NULL_MAP },
  161. X    { C_LAST_MSG,    "last-msg",        NULL, NULL_MAP },
  162. X    { C_TOP_PAGE,    "top-page",        NULL, NULL_MAP },
  163. X    { C_BOTTOM_PAGE,    "bottom-page",        NULL, NULL_MAP },
  164. X    { C_NEXT_SCREEN,    "screen-next",        NULL, NULL_MAP },
  165. X    { C_PREV_SCREEN,    "screen-back",        NULL, NULL_MAP },
  166. X    { C_SOURCE,        "source",        NULL, NULL_MAP },
  167. X    { C_SAVEOPTS,    "saveopts",        NULL, NULL_MAP },
  168. X    { C_NEXT_SEARCH,    "search-next",        NULL, NULL_MAP },
  169. X    { C_PREV_SEARCH,    "search-back",        NULL, NULL_MAP },
  170. X    { C_CONT_SEARCH,    "search-again",        NULL, NULL_MAP },
  171. X    { C_PRESERVE,    "preserve",        NULL, NULL_MAP },
  172. X    { C_REV_SORT,    "sort-reverse",        NULL, NULL_MAP },
  173. X    { C_SORT,        "sort",            NULL, NULL_MAP },
  174. X    { C_QUIT_HARD,    "quit!",        NULL, NULL_MAP },
  175. X    { C_QUIT,        "quit",            NULL, NULL_MAP },
  176. X    { C_EXIT_HARD,    "exit!",        NULL, NULL_MAP },
  177. X    { C_EXIT,        "exit",            NULL, NULL_MAP },
  178. X    { C_UPDATE,        "update",        NULL, NULL_MAP },
  179. X    { C_FOLDER,        "folder",        NULL, NULL_MAP },
  180. X    { C_SHELL_ESC,    "shell-escape",        NULL, NULL_MAP },
  181. X    { C_CURSES_ESC,    "line-mode",        NULL, NULL_MAP },
  182. X    { C_PRINT_MSG,    "lpr",            NULL, NULL_MAP },
  183. X    { C_CHDIR,        "chdir",        NULL, NULL_MAP },
  184. X    { C_VAR_SET,    "variable",        NULL, NULL_MAP },
  185. X    { C_IGNORE,        "ignore",        NULL, NULL_MAP },
  186. X    { C_ALIAS,        "alias",        NULL, NULL_MAP },
  187. X    { C_OWN_HDR,    "my-hdrs",        NULL, NULL_MAP },
  188. X    { C_VERSION,    "version",        NULL, NULL_MAP },
  189. X    { C_MAIL_FLAGS,    "mail-flags",        NULL, NULL_MAP },
  190. X    { C_MAIL,        "mail",            NULL, NULL_MAP },
  191. X    { C_REPLY_ALL,    "reply-all",        NULL, NULL_MAP },
  192. X    { C_REPLY_SENDER,    "reply",        NULL, NULL_MAP },
  193. X    { C_DISPLAY_NEXT,    "display-next",        NULL, NULL_MAP },
  194. X    { C_DISPLAY_MSG,    "display",        NULL, NULL_MAP },
  195. X    { C_TOP_MSG,    "top",            NULL, NULL_MAP },
  196. X    { C_BIND_MACRO,    "bind-macro",        NULL, NULL_MAP },
  197. X    { C_BIND,        "bind",            NULL, NULL_MAP },
  198. X    { C_UNBIND,        "unbind",        NULL, NULL_MAP },
  199. X    { C_MAP_BANG,    "map!",            NULL, NULL_MAP },
  200. X    { C_MAP,        "map",            NULL, NULL_MAP },
  201. X    { C_MACRO,        "macro",        NULL, NULL_MAP },
  202. X    /* C_HELP Must be the last one! */
  203. X    { C_HELP,        "help",            NULL, NULL_MAP }
  204. X};
  205. X
  206. X#ifdef CURSES
  207. X
  208. X/*
  209. X * getcmd() is called from curses mode only.  It waits for char input from
  210. X * the user via m_getchar() (which means that a macro could provide input)
  211. X * and then compares the chars input against the "bind"ings set up by the
  212. X * user (or the defaults).  For example, 'j' could bind to "next msg" which
  213. X * is interpreted by the big switch statement in curses_command() (curses.c).
  214. X * getcmd() returns the int-value of the curses command the input is "bound"
  215. X * to.  If the input is unrecognized, C_NULL is returned (curses_command()
  216. X * might require some cleanup, so this is valid, too).
  217. X *
  218. X * Since the input could originate from a macro rather than the terminal,
  219. X * check to see if this is the case and search for a '[' char which indicates
  220. X * that there is a curses command or other "long" command to be executed.
  221. X */
  222. Xgetcmd()
  223. X{
  224. X    char         buf[MAX_BIND_LEN * 3];
  225. X    register int     c, m, match;
  226. X    register char    *p = buf;
  227. X    register struct cmd_map *list;
  228. X
  229. X    bzero(buf, MAX_BIND_LEN);
  230. X    active_cmd = NULL_MAP;
  231. X    c = m_getchar();
  232. X    /* If user did job control (^Z), then the interrupt flag will be
  233. X     * set.  Be sure it's unset before continuing.
  234. X     */
  235. X    turnoff(glob_flags, WAS_INTR);
  236. X    if (isdigit(c)) {
  237. X    buf[0] = c;
  238. X    buf[1] = '\0';
  239. X    Ungetstr(buf); /* So mac_flush can clear on error */
  240. X    return C_GOTO_MSG;
  241. X    }
  242. X    for (;;) {
  243. X    if (ison(glob_flags, IN_MACRO) && c == MAC_LONG_CMD)
  244. X        return long_mac_cmd(c, TRUE);
  245. X    else
  246. X        *p++ = c;
  247. X    m = 0;
  248. X    for (list = cmd_map; list; list = list->m_next) {
  249. X        if ((match = prefix(buf, list->m_str)) == MATCH) {
  250. X        if (debug)
  251. X            print("\"%s\" ",
  252. X            ctrl_strcpy(buf,
  253. X                    map_func_names[list->m_cmd].m_str,
  254. X                    TRUE));
  255. X        if (list->m_cmd == C_MACRO) {
  256. X            curs_macro(list->x_str);
  257. X            return getcmd();
  258. X        }
  259. X        active_cmd = list;
  260. X        return (int)list->m_cmd;
  261. X        } else if (match != NO_MATCH)
  262. X        m++;
  263. X    }
  264. X    if (m == 0) {
  265. X        if (debug) {
  266. X        char tmp[sizeof buf];
  267. X        print("No binding for \"%s\" found.",
  268. X            ctrl_strcpy(tmp, buf, TRUE));
  269. X        }
  270. X        return C_NULL;
  271. X    }
  272. X    c = m_getchar();
  273. X    }
  274. X}
  275. X
  276. X#endif /* CURSES */
  277. X
  278. X/*
  279. X * bind_it() is used to set or unset bind, map and map! settings.
  280. X * bind is used to accelerate curses commands by mapping key sequences
  281. X * to curses commands.  map is used to accelerate command mode keysequences
  282. X * by simulating stdin.  map! is the same, but used when in compose mode.
  283. X *
  284. X * bind_it() doesn't touch messages; return -1 for curses mode.
  285. X * return -2 to have curses command set CNTD_CMD to prevent screen refresh
  286. X * to allow user to read output in case of multiple lines.
  287. X *
  288. X * Since this routine deals with a lot of binding and unbinding of things
  289. X * like line-mode "map"s and is interactive (calls Getstr()), be very careful
  290. X * not to allow expansions during interaction.
  291. X */
  292. Xbind_it(len, argv)
  293. Xchar **argv;
  294. X{
  295. X    char string[MAX_BIND_LEN], buf[256], *name = NULL;
  296. X    char *rawstr; /* raw format of string (ptr to string if no argv avail) */
  297. X    char ascii[MAX_BIND_LEN*2]; /* printable ascii version of string */
  298. X    register int x;
  299. X    SIGRET (*oldint)(), (*oldquit)();
  300. X    struct cmd_map **map_list;
  301. X    int unbind = (argv && **argv == 'u');
  302. X    int map = 0, is_bind_macro = 0;
  303. X    int ret = 0 - iscurses; /* return value */
  304. X
  305. X    if (argv && !strcmp(name = *argv, "bind-macro"))
  306. X    is_bind_macro++;
  307. X
  308. X    if (map = (argv && (!strcmp(name, "map!") || !strcmp(name, "unmap!"))))
  309. X    map_list = &bang_map;
  310. X    else if (map = (argv && (!strcmp(name, "map") || !strcmp(name, "unmap"))))
  311. X    map_list = &line_map;
  312. X    else
  313. X    map_list = &cmd_map;
  314. X
  315. X    if (argv && *++argv && !strcmp(*argv, "-?"))
  316. X    /* Subtract ret and iscurses to signal output */
  317. X    return help(0, name, cmd_help) - ret - iscurses;
  318. X
  319. X    if (iscurses)
  320. X    on_intr();
  321. X
  322. X    if (unbind) {
  323. X    if (!*argv) {
  324. X        print("%s what? ", name);
  325. X        len = Getstr(buf, sizeof buf, 0);
  326. X        if (len <= 0) {
  327. X        if (iscurses)
  328. X            off_intr();
  329. X        return -1;
  330. X        }
  331. X        rawstr = m_xlate(buf);
  332. X    } else
  333. X        rawstr = m_xlate(*argv);
  334. X    if (!un_bind(rawstr, map_list)) {
  335. X        ctrl_strcpy(ascii, rawstr, TRUE);
  336. X        print("\"%s\" isn't bound to a command.\n", ascii);
  337. X    }
  338. X    if (iscurses)
  339. X        off_intr();
  340. X    return ret;
  341. X    }
  342. X    if (argv && *argv) {
  343. X    rawstr = m_xlate(*argv);
  344. X    (void) ctrl_strcpy(ascii, rawstr, TRUE);
  345. X    if (!*++argv) {
  346. X        /*
  347. X         * determine whether "argv" references a "map" or a "bind"
  348. X         */
  349. X        int binding = c_bind(rawstr, *map_list);
  350. X        if (binding == C_MACRO) {
  351. X        char *mapping = c_macro(NULL, rawstr, *map_list);
  352. X        if (mapping) {
  353. X            print("\"%s\" is mapped to ", ascii);
  354. X            print_more("\"%s\".\n",
  355. X            ctrl_strcpy(buf, mapping, FALSE));
  356. X        } else
  357. X            print("\"%s\" isn't mapped.\n", ascii);
  358. X        } else if (binding)
  359. X        print("\"%s\" is %s to \"%s\".\n", ascii,
  360. X            map? "mapped" : "bound", map_func_names[binding].m_str);
  361. X        else if (map)
  362. X        print("\"%s\" isn't mapped.\n", ascii);
  363. X        else
  364. X        print("\"%s\" isn't bound to a command.\n", ascii);
  365. X        if (iscurses)
  366. X        off_intr();
  367. X        return ret;
  368. X    }
  369. X    } else {
  370. X    print("%s [<CR>=all, -?=help]: ", name);
  371. X    len = Getstr(string, MAX_BIND_LEN-1, 0);
  372. X    if (len == 0) {
  373. X        int add_to_ret = iscurses;
  374. X#ifdef CURSES
  375. X        if (iscurses)
  376. X        move(LINES-1, 0), refresh();
  377. X#endif
  378. X        if (map || is_bind_macro)
  379. X        add_to_ret = !c_macro(name, NULL, *map_list);
  380. X        else
  381. X        add_to_ret = !c_bind(NULL, *map_list);
  382. X        if (iscurses)
  383. X        off_intr();
  384. X        /* signal CTND_CMD if there was output */
  385. X        return ret - add_to_ret;
  386. X    }
  387. X    if (len < 0) {
  388. X        if (iscurses)
  389. X        off_intr();
  390. X        return ret;
  391. X    }
  392. X    rawstr = m_xlate(string);
  393. X    (void) ctrl_strcpy(ascii, rawstr, TRUE);
  394. X    }
  395. X    /* if a binding was given on the command line */
  396. X    if (argv && *argv && !map)
  397. X    if (is_bind_macro)
  398. X        (void) strcpy(buf, "macro");
  399. X    else
  400. X        (void) strcpy(buf, *argv++);
  401. X    else {
  402. X    /* at this point, "rawstr" and "ascii" should both be set */
  403. X    int binding;
  404. X
  405. X    if (!strcmp(ascii, "-?")) {
  406. X        if (iscurses)
  407. X        clr_bot_line();
  408. X        ret -= help(0, name, cmd_help);
  409. X        if (iscurses)
  410. X        off_intr();
  411. X        /* Subtract iscurses to signal CNTD_CMD */
  412. X        return ret - iscurses;
  413. X    }
  414. X
  415. X    if (!map && !is_bind_macro) {
  416. X        binding = c_bind(rawstr, *map_list);
  417. X
  418. X        for (len = 0; len == 0; ) {
  419. X        print("\"%s\" = <%s>: New binding [<CR> for list]: ",
  420. X            ascii, (binding? map_func_names[binding].m_str : "unset"));
  421. X        len = Getstr(buf, sizeof buf, 0);
  422. X        if (iscurses)
  423. X            clr_bot_line();
  424. X        /* strip any trailing whitespace */
  425. X        if (len > 0)
  426. X            len = no_newln(buf) - buf;
  427. X        if (len == 0) {
  428. X            (void) do_pager(NULL, TRUE);
  429. X            if (iscurses)
  430. X            putchar('\n');
  431. X            for (x = 1; x <= C_HELP; x++) {
  432. X            if (!(x % 4))
  433. X                if (do_pager("\n", FALSE) == EOF)
  434. X                break;
  435. X            (void) do_pager(sprintf(buf, "%-15.15s  ",
  436. X                        map_func_names[x].m_str), FALSE);
  437. X            }
  438. X            (void) do_pager("\n", FALSE);
  439. X            (void) do_pager(NULL, FALSE);
  440. X            ret -= iscurses;
  441. X        }
  442. X        }
  443. X    } else /* map */
  444. X        (void) strcpy(buf, "macro"), len = 5;
  445. X    /* if list was printed, ret < -1 -- tells CNTD_CMD to be set and
  446. X     * prevents screen from being refreshed (lets user read output
  447. X     */
  448. X    if (len == -1) {
  449. X        if (iscurses)
  450. X        off_intr();
  451. X        return ret;
  452. X    }
  453. X    }
  454. X    for (x = 1; x <= C_HELP; x++) {
  455. X    if (prefix(buf, map_func_names[x].m_str) == MATCH) {
  456. X        int add_to_ret;
  457. X        if (debug)
  458. X        print("\"%s\" will execute \"%s\".\n", ascii, buf);
  459. X        if (map_func_names[x].m_cmd == C_MACRO) {
  460. X        if (argv && *argv) {
  461. X            (void) argv_to_string(buf, argv);
  462. X            (void) m_xlate(buf); /* Convert buf to raw chars */
  463. X            add_to_ret =
  464. X            do_bind(rawstr, map_func_names[x].m_cmd, buf, map_list);
  465. X        } else {
  466. X            char exp[MAX_MACRO_LEN*2]; /* printable expansion */
  467. X            char *mapping = c_macro(NULL, rawstr, *map_list);
  468. X
  469. X            if (mapping)
  470. X            (void) ctrl_strcpy(exp, mapping, TRUE);
  471. X            print("\"%s\" = <%s>", ascii, mapping ? exp : "unset");
  472. X            putchar('\n'), print("New macro: ");
  473. X            ret -= iscurses; /* To signal screen messed up */
  474. X            /* we are done with buf, so we can trash over it */
  475. X            len = Getstr(buf, MAX_MACRO_LEN, 0);
  476. X            if (len > 0) {
  477. X            if (iscurses)
  478. X                clr_bot_line();
  479. X            (void) m_xlate(buf); /* Convert buf to raw chars */
  480. X            add_to_ret =
  481. X                do_bind(rawstr, C_MACRO, buf, map_list);
  482. X            if (debug) {
  483. X                (void) ctrl_strcpy(exp, buf, TRUE);
  484. X                print("\"%s\" will execute \"%s\".\n", ascii, exp);
  485. X            }
  486. X            } else if (len < 0) {
  487. X            if (iscurses)
  488. X                off_intr();
  489. X            return ret - add_to_ret;
  490. X            } else
  491. X            print("Can't bind to null macro"), putchar('\n');
  492. X        }
  493. X        } else /* not a macro */ {
  494. X        (void) argv_to_string(buf, argv);
  495. X        add_to_ret =
  496. X            do_bind(rawstr, map_func_names[x].m_cmd, buf, map_list);
  497. X        }
  498. X        /* if do_bind had no errors, it returned -1.  If we already
  499. X         * messed up the screen, then ret is less than -1.  return the
  500. X         * lesser of the two to make sure that CNTD_CMD gets set right
  501. X         */
  502. X        if (iscurses)
  503. X        off_intr();
  504. X        return min(add_to_ret, ret);
  505. X    }
  506. X    }
  507. X    print("\"%s\": Unknown function.\n", buf);
  508. X    if (iscurses)
  509. X    off_intr();
  510. X    return ret;
  511. X}
  512. X
  513. X/*
  514. X * print current key to command bindings if "str" is NULL.
  515. X * else return the integer "m_cmd" which the str is bound to.
  516. X */
  517. Xc_bind(str, opts)
  518. Xregister char *str;
  519. Xregister struct cmd_map *opts;
  520. X{
  521. X    register int    incurses = iscurses;
  522. X
  523. X    if (!str) {
  524. X    if (!opts) {
  525. X        print("No command bindings.\n");
  526. X        return C_ERROR;
  527. X    }
  528. X    if (incurses)
  529. X        clr_bot_line(), iscurses = FALSE;
  530. X    (void) do_pager(NULL, TRUE);
  531. X    (void) do_pager("Current key to command bindings:\n", FALSE);
  532. X    (void) do_pager("\n", FALSE);
  533. X    }
  534. X
  535. X    for (; opts; opts = opts->m_next) {
  536. X    char buf[BUFSIZ], buf2[MAX_BIND_LEN], exp[MAX_MACRO_LEN*2], *xp;
  537. X    if (!str) {
  538. X        (void) ctrl_strcpy(buf2, opts->m_str, FALSE);
  539. X        if ((xp = opts->x_str) && opts->m_cmd == C_MACRO)
  540. X        xp = ctrl_strcpy(exp, opts->x_str, TRUE);
  541. X        if (do_pager(sprintf(buf, "%s\t%-15.15s %s\n",
  542. X             buf2, map_func_names[opts->m_cmd].m_str,
  543. X             xp? xp : ""),
  544. X             FALSE) == EOF)
  545. X        break;
  546. X    } else
  547. X        if (strcmp(str, opts->m_str))
  548. X        continue;
  549. X        else
  550. X        return opts->m_cmd;
  551. X    }
  552. X
  553. X    iscurses = incurses;
  554. X    if (!str)
  555. X    (void) do_pager(NULL, FALSE);
  556. X    return C_NULL;
  557. X}
  558. X
  559. X/*
  560. X * Doesn't touch messages, but changes macros: return -1.
  561. X * Error output causes return < -1.
  562. X *  args is currently the execute string of a macro mapping, but may be
  563. X *  used in the future as an argument string for any curses command.
  564. X */
  565. Xdo_bind(str, func, args, map_list)
  566. Xregister char *str, *args;
  567. Xstruct cmd_map **map_list;
  568. Xlong func;
  569. X{
  570. X    register int ret = -1;
  571. X    register struct cmd_map *list;
  572. X    int match;
  573. X
  574. X    if (func == C_MACRO && !check_mac_bindings(args))
  575. X    --ret;
  576. X    (void) un_bind(str, map_list);
  577. X    for (list = *map_list; list; list = list->m_next)
  578. X    if ((match = prefix(str, list->m_str)) != NO_MATCH) {
  579. X        ret--;
  580. X        switch (match) {
  581. X        case MATCH:
  582. X            puts("Something impossible just happened.");
  583. X        when A_PREFIX_B:
  584. X            wprint("Warning: \"%s\" prefixes \"%s\" (%s)\n", str,
  585. X            list->m_str, map_func_names[list->m_cmd].m_str);
  586. X        when B_PREFIX_A:
  587. X            wprint("Warning: \"%s\" (%s) prefixes: \"%s\"\n",
  588. X            list->m_str, map_func_names[list->m_cmd].m_str, str);
  589. X        }
  590. X    }
  591. X    add_bind(str, func, args, map_list);
  592. X    /* errors decrement ret.  If ret returns less than -1, CNTD_CMD is set
  593. X     * and no redrawing is done so user can see the warning signs
  594. X     */
  595. X    return ret;
  596. X}
  597. X
  598. X/*
  599. X * add a binding to a list.  This may include "map"s or other mappings since
  600. X * the map_list argument can control that.  The "func" is an int defined in
  601. X * bindings.h ... the "str" passed is the string the user would have to type
  602. X * to get the macro/map/binding expanded.  This must in in raw format: no
  603. X * \n's to mean \015.  Convert first using m_xlate().
  604. X */
  605. Xadd_bind(str, func, args, map_list)
  606. Xregister char *str, *args;
  607. Xstruct cmd_map **map_list;
  608. Xlong func;
  609. X{
  610. X    register struct cmd_map *tmp;
  611. X    struct cmd_map *calloc();
  612. X
  613. X    if (!str || !*str)
  614. X    return;
  615. X
  616. X    /* now make a new option struct and set fields */
  617. X    if (!(tmp = calloc((unsigned)1, sizeof(struct cmd_map)))) {
  618. X    error("calloc");
  619. X    return;
  620. X    }
  621. X    tmp->m_next = *map_list;
  622. X    *map_list = tmp;
  623. X
  624. X    tmp->m_str = savestr(str);
  625. X    tmp->m_cmd = func; /* strdup handles the NULL case */
  626. X    if (args && *args)
  627. X    tmp->x_str = savestr(args);
  628. X    else
  629. X    tmp->x_str = NULL;
  630. X}
  631. X
  632. Xstatic
  633. Xun_bind(p, map_list)
  634. Xregister char *p;
  635. Xstruct cmd_map **map_list;
  636. X{
  637. X    register struct cmd_map *list = *map_list, *tmp;
  638. X
  639. X    if (!list || !*list->m_str || !p || !*p)
  640. X    return 0;
  641. X
  642. X    if (!strcmp(p, (*map_list)->m_str)) {
  643. X    *map_list = (*map_list)->m_next;
  644. X    xfree (list->m_str);
  645. X    if (list->x_str)
  646. X        xfree (list->x_str);
  647. X    xfree((char *)list);
  648. X    return 1;
  649. X    }
  650. X    for ( ; list->m_next; list = list->m_next)
  651. X    if (!strcmp(p, list->m_next->m_str)) {
  652. X        tmp = list->m_next;
  653. X        list->m_next = list->m_next->m_next;
  654. X        xfree (tmp->m_str);
  655. X        if (tmp->x_str)
  656. X        xfree (tmp->x_str);
  657. X        xfree ((char *)tmp);
  658. X        return 1;
  659. X    }
  660. X    return 0;
  661. X}
  662. X
  663. Xprefix(a, b)
  664. Xregister char *a, *b;
  665. X{
  666. X    if (!a || !b)
  667. X    return NO_MATCH;
  668. X
  669. X    while (*a && *b && *a == *b)
  670. X    a++, b++;
  671. X    if (!*a && !*b)
  672. X    return MATCH;
  673. X    if (!*a && *b)
  674. X    return A_PREFIX_B;
  675. X    if (*a && !*b)
  676. X    return B_PREFIX_A;
  677. X    return NO_MATCH;
  678. X}
  679. END_OF_FILE
  680. if test 20444 -ne `wc -c <'bind.c'`; then
  681.     echo shar: \"'bind.c'\" unpacked with wrong size!
  682. fi
  683. # end of 'bind.c'
  684. fi
  685. if test -f 'hdrs.c' -a "${1}" != "-c" ; then 
  686.   echo shar: Will not clobber existing file \"'hdrs.c'\"
  687. else
  688. echo shar: Extracting \"'hdrs.c'\" \(18781 characters\)
  689. sed "s/^X//" >'hdrs.c' <<'END_OF_FILE'
  690. X/* hdrs.c     (c) copyright 1986 (Dan Heller) */
  691. X
  692. X/*
  693. X * Routines that deal with message headers inside messages
  694. X * msg_get(n, from, count) -- get the From_ line in msg n into "from".
  695. X * header_field(n, str) -- get the header named "str" from msg n.
  696. X * do_hdrs(argc, argv, list) -- diplay message headers.
  697. X * specl_hdrs(argv, list) -- display msgs that share common attributes.
  698. X * compose_hdr(cnt) -- compose a message header from msg n.
  699. X * reply_to(n, all, buf) -- construct a header based on the To: header of n.
  700. X * subject_to(n, buf) -- get the subject for replying to msg n.
  701. X * cc_to(n, buf) -- construct a Cc header based on the Cc of message n.
  702. X */
  703. X#include "mush.h"
  704. X
  705. X/*
  706. X * Get a message from the current folder by its offset.
  707. X * Copy the From_ line to the second argument if the third arg > 0,
  708. X * and return the second argument, or NULL on an error.
  709. X */
  710. Xchar *
  711. Xmsg_get(n, from, count)
  712. Xint n, count;
  713. Xchar *from;
  714. X{
  715. X    if (fseek(tmpf, msg[n].m_offset, L_SET) == -1) {
  716. X    error("fseek in %s (msg %d, folder=%s)", tempfile, n+1, mailfile);
  717. X    turnon(glob_flags, READ_ONLY);
  718. X    return NULL;
  719. X    }
  720. X    if (count)
  721. X#ifndef MSG_SEPARATOR
  722. X    return fgets(from, count, tmpf);
  723. X#else
  724. X    *from = '\0';
  725. X#endif
  726. X    return from;
  727. X}
  728. X
  729. X/*
  730. X * get which message via the offset and search for the headers which
  731. X * match the string "str". there may be more than one of a field (like Cc:)
  732. X * so get them all and "cat" them together into the static buffer
  733. X * "buf" and return its address.
  734. X */
  735. Xchar *
  736. Xheader_field(n, str)
  737. Xchar *str;
  738. X{
  739. X    static char    buf[HDRSIZ];
  740. X    char        tmp[HDRSIZ];
  741. X    register char  *p, *p2, *b = buf;
  742. X    int contd_hdr;  /* true if next line is a continuation of the hdr we want */
  743. X
  744. X    if (!msg_get(n, tmp, sizeof tmp))
  745. X    return NULL;
  746. X    *b = 0;
  747. X    while((p = fgets(tmp, sizeof(tmp), tmpf)) && *p != '\n') {
  748. X    if (*p != ' ' && *p != '\t') {
  749. X        contd_hdr = 0;
  750. X        /* strcmp ignoring case */
  751. X        for(p2 = str; *p && *p2 && *p2 == lower(*p); ++p, ++p2);
  752. X        /* MATCH is true if p2 is at the end of str and *p is ':' */
  753. X        if (*p2 || *p++ != ':')
  754. X        continue;
  755. X        else
  756. X        contd_hdr = 1;
  757. X    } else if (!contd_hdr)
  758. X        continue;
  759. X    skipspaces(0);
  760. X    p2 = no_newln(p);
  761. X    *++p2 = ',', *++p2 = ' ', *++p2 = 0;
  762. X    if (strlen(p) + (b-buf) < sizeof (buf))
  763. X        b += Strcpy(b, p);
  764. X    }
  765. X    if (b > buf) /* now get rid of the trailing ", " */
  766. X    *--b = 0, *--b = 0;
  767. X    return (*buf)? buf: NULL;
  768. X}
  769. X
  770. Xdo_hdrs(argc, argv, list)
  771. Xregister char **argv, list[];
  772. X{
  773. X    register int   pageful = 0;
  774. X    SIGRET        (*oldint)(), (*oldquit)();
  775. X    int           show_deleted;
  776. X    static int     cnt;
  777. X    register char  *p;
  778. X    char        first_char = (argc) ? **argv: 'h';
  779. X
  780. X    if (argc > 1 && !strcmp(argv[1], "-?"))
  781. X    return help(0, "headers", cmd_help);
  782. X
  783. X    if (!msg_cnt) {
  784. X    if (ison(glob_flags, DO_PIPE))
  785. X        return 0;
  786. X#ifdef CURSES
  787. X    if (iscurses)
  788. X        clear();
  789. X#endif /* CURSES */
  790. X#ifdef SUNTOOL
  791. X    if (istool)
  792. X        mail_status(0);
  793. X#endif /* SUNTOOL */
  794. X    return 0;
  795. X    }
  796. X    if (first_char == ':' || (argc > 1 && argv[1][0] == ':')) {
  797. X    if (first_char != ':')
  798. X        argv++;
  799. X    return specl_hdrs(argv, list);
  800. X    }
  801. X
  802. X    on_intr();
  803. X
  804. X    if (argc && (argv[0][1] == '+' || argc > 1 && !strcmp(argv[1], "+")) ||
  805. X        first_char == 'z' && !argv[1])
  806. X    if (msg_cnt > screen)
  807. X        cnt = min(msg_cnt - screen, n_array[0] + screen);
  808. X    else
  809. X        cnt = 0;
  810. X    else if (argc && (argv[0][1] == '-' || argc > 1 && !strcmp(argv[1], "-")))
  811. X    cnt = max((cnt - 2*screen), 0);
  812. X    else if (argc && *++argv &&
  813. X    (isdigit(**argv) || **argv == '^' || **argv == '$' || **argv == '.')
  814. X     || ison(glob_flags, IS_PIPE)) {
  815. X    /* if we're coming from a pipe, start display at the first msg bit
  816. X     * set in the msg_list
  817. X     */
  818. X    int fnd;
  819. X    if (ison(glob_flags, IS_PIPE)) {
  820. X        if (isoff(glob_flags, DO_PIPE))
  821. X        for (fnd = 0; fnd < msg_cnt; fnd++)
  822. X            if (msg_bit(list, fnd))
  823. X            wprint("%s\n", compose_hdr(fnd));
  824. X        off_intr();
  825. X        return 0;
  826. X    }
  827. X    /* if a number was given, use it */
  828. X    if (!(fnd = chk_msg(*argv))) {
  829. X        off_intr();
  830. X        return -1;
  831. X    }
  832. X    for (cnt = fnd - 1; cnt > 0 && cnt + screen > msg_cnt; cnt--)
  833. X        ;
  834. X    } else if (current_msg < n_array[0] || current_msg > n_array[screen-1])
  835. X    cnt = current_msg; /* adjust if reads have passed screen bounds */
  836. X    else if (cnt >= msg_cnt || !argc || !*argv)
  837. X    cnt = max((cnt - screen), 0); /* adjust window to maintain position */
  838. X
  839. X    show_deleted = !!do_set(set_options, "show_deleted");
  840. X
  841. X    /* Make sure we have at least $screen headers to print */
  842. X    if (cnt > 0 && !iscurses && !show_deleted && first_char != 'h') {
  843. X    int tmp = cnt;
  844. X    /* first count how many messages we can print without adjusting */
  845. X    for (pageful = 0; pageful < screen && cnt < msg_cnt; cnt++)
  846. X        if (isoff(msg[cnt].m_flags, DELETE))
  847. X        pageful++;
  848. X    /* if we can't print a pagefull of hdrs, back up till we can */
  849. X    for (cnt = tmp; pageful < screen && cnt; --cnt)
  850. X        if (isoff(msg[cnt].m_flags, DELETE))
  851. X        pageful++;
  852. X    pageful = 0;    /* Used later as an index, so reset */
  853. X    }
  854. X
  855. X    for (;pageful<screen && cnt<msg_cnt && isoff(glob_flags, WAS_INTR); cnt++) {
  856. X    if (!iscurses && !show_deleted && first_char == 'h'
  857. X        && ison(msg[cnt].m_flags, DELETE))
  858. X        continue;
  859. X    n_array[pageful++] = cnt;
  860. X    /* this message was displayed -- set the bit */
  861. X    if (list)
  862. X        set_msg_bit(list, cnt);
  863. X    /* if do_pipe, don't output anything */
  864. X    if (ison(glob_flags, DO_PIPE))
  865. X        continue;
  866. X    p = compose_hdr(cnt);
  867. X    if (!istool && (!iscurses || ison(glob_flags, IS_GETTING)))
  868. X        puts(p);
  869. X#ifdef SUNTOOL
  870. X    else if (istool) {
  871. X        if (cnt == current_msg) /* embolden or reverse-video */
  872. X        highlight(hdr_win, 0,pageful*l_height(DEFAULT), DEFAULT,p);
  873. X        else
  874. X        pw_text(hdr_win, 0, pageful * l_height(DEFAULT), PIX_SRC,
  875. X                            fonts[DEFAULT], p);
  876. X        Clrtoeol(hdr_win, strlen(p)*l_width(DEFAULT),
  877. X             pageful*l_height(DEFAULT), DEFAULT);
  878. X    }
  879. X#endif /* SUNTOOL */
  880. X#ifdef CURSES
  881. X        else if (iscurses) {
  882. X        move(pageful, 0);
  883. X        printw("%-.*s", COLS-2, p), clrtoeol();
  884. X        }
  885. X#endif /* CURSES */
  886. X    }
  887. X    /* just in case a signal stopped us */
  888. X    off_intr();
  889. X    pageful++;
  890. X#ifdef CURSES
  891. X    if (iscurses && pageful < screen)
  892. X    move(pageful, 0), clrtobot();
  893. X#endif /* CURSES */
  894. X    if (cnt == msg_cnt) {
  895. X    while (pageful <= screen) {
  896. X        n_array[pageful-1] = msg_cnt+1; /* assign out-of-range values */
  897. X#ifdef SUNTOOL
  898. X        if (istool)
  899. X        Clrtoeol(hdr_win, 0, pageful * l_height(DEFAULT), DEFAULT);
  900. X#endif /* SUNTOOL */
  901. X        ++pageful;
  902. X    }
  903. X    }
  904. X#ifdef SUNTOOL
  905. X    if (istool) {
  906. X    if (msg_cnt > screen) {
  907. X        panel_set(next_scr, PANEL_SHOW_ITEM, TRUE, 0);
  908. X        panel_set(prev_scr, PANEL_SHOW_ITEM, TRUE, 0);
  909. X    }
  910. X    mail_status(0);
  911. X    }
  912. X#endif /* SUNTOOL */
  913. X    return 0;
  914. X}
  915. X
  916. X#define NEW 1
  917. X#define ALL 2
  918. X
  919. Xspecl_hdrs(argv, list)
  920. Xchar **argv, list[];
  921. X{
  922. X    u_long    special = 0;
  923. X    int     n = 0;
  924. X
  925. X    while (argv[0][++n])
  926. X    switch(argv[0][n]) {
  927. X        case 'a': special = ALL;
  928. X        when 'n': special = NEW;
  929. X        when 'u': special = UNREAD;
  930. X        when 'o': special = OLD;
  931. X        when 'd': special = DELETE;
  932. X        when 'r': special = REPLIED;
  933. X        when 's': special = SAVED;
  934. X        when 'p': special = PRESERVE;
  935. X        otherwise: print("choose from n,u,o,d,r,s,p or a"); return -1;
  936. X    }
  937. X    if (debug)
  938. X    (void) check_flags(special);
  939. X
  940. X    for (n = 0; n < msg_cnt; n++) {
  941. X    /*
  942. X     * First, see if we're looking for NEW messages.
  943. X     * If so, then check to see if the msg is unread and not old.
  944. X     * If special > ALL, then special has a mask of bits describing
  945. X     * the state of the message.
  946. X     */
  947. X    if (ison(glob_flags, IS_PIPE)&& !msg_bit(list, n))
  948. X        continue;
  949. X    if (special == ALL || special == NEW &&
  950. X           (ison(msg[n].m_flags, UNREAD) && isoff(msg[n].m_flags, OLD))) {
  951. X        if (isoff(glob_flags, DO_PIPE))
  952. X        print("%s\n", compose_hdr(n));
  953. X        if (list)
  954. X        set_msg_bit(list, n);
  955. X#ifndef M_XENIX
  956. X    /*
  957. X     * XENIX compiler can't handle "special" in ison() macro.
  958. X     * It only works if the second argument is a constant!
  959. X     */
  960. X    } else if (special > ALL && ison(msg[n].m_flags, special)) {
  961. X        if (isoff(glob_flags, DO_PIPE))
  962. X        print("%s\n", compose_hdr(n));
  963. X        if (list)
  964. X        set_msg_bit(list, n);
  965. X#endif /* M_XENIX */
  966. X    } else {
  967. X        if (list)
  968. X        unset_msg_bit(list, n);
  969. X        if (debug) {
  970. X        printf("msg[%d].m_flags: %d", n, msg[n].m_flags);
  971. X        (void) check_flags(msg[n].m_flags);
  972. X        }
  973. X    }
  974. X    }
  975. X    return 0;
  976. X}
  977. X
  978. X#define Strncpy(buf,p) (void)(strncpy(buf,p,sizeof(buf)),buf[sizeof(buf)-1]=0)
  979. X
  980. X/*
  981. X * compose a header from the information about a message (from, to, date,
  982. X * subject, etc..).  The header for message number "cnt" is built and is
  983. X * returned in the static buffer "buf".  There will be *at least* 9 chars
  984. X * in the buffer which will be something like: " 123 >N " The breakdown
  985. X * is as follows: 4 chars for the message number, 1 space, 1 char for '>'
  986. X * (if current message) and two spaces for message status (new, unread, etc)
  987. X * followed by 1 terminating space.
  988. X * Read other comments in the routine for more info.
  989. X */
  990. Xchar *
  991. Xcompose_hdr(cnt)
  992. X{
  993. X    static char        buf[256];
  994. X    register char    *p, *p2, *b;
  995. X    int            len, do_pad = FALSE, val, pad, got_dot, isauthor = 0, n;
  996. X    char from[HDRSIZ], subject[256], date[64], lines[16];
  997. X    char to[256], addr[256], name[256], status[4];
  998. X    char Day[3], Mon[4], Tm[8], Yr[5], Wkday[4], *date_p;
  999. X
  1000. X    /* status of the message */
  1001. X    if (ison(msg[cnt].m_flags, DELETE))
  1002. X    status[0] = '*';
  1003. X    else if (ison(msg[cnt].m_flags, PRESERVE))
  1004. X    status[0] = 'P';
  1005. X    else if (ison(msg[cnt].m_flags, SAVED))
  1006. X    status[0] = 'S';
  1007. X    else if (ison(msg[cnt].m_flags, OLD) && ison(msg[cnt].m_flags, UNREAD))
  1008. X    status[0] = 'U';
  1009. X    else if (isoff(msg[cnt].m_flags, UNREAD))
  1010. X    status[0] = ' ';
  1011. X    else
  1012. X    status[0] = 'N';
  1013. X
  1014. X    if (ison(msg[cnt].m_flags, REPLIED))
  1015. X    status[1] = 'r';
  1016. X    else
  1017. X    status[1] = ' ';
  1018. X
  1019. X    to[0] = from[0] = subject[0] = date[0] = lines[0] = addr[0] =
  1020. X    name[0] = Day[0] = Mon[0] = Tm[0] = Yr[0] = Wkday[0] = 0;
  1021. X
  1022. X    /* who's the message to */
  1023. X    if ((p = header_field(cnt, "resent-to")) ||
  1024. X    (p = header_field(cnt, "to")) ||
  1025. X    (p = header_field(cnt, "apparently-to")))
  1026. X    Strncpy(to, p);
  1027. X
  1028. X    /* who's the message from */
  1029. X    if ((p = header_field(cnt, "from")) && strcpy(from, p)
  1030. X        || (p = reply_to(cnt, 0, from))) {
  1031. X    /* NOTE:  this fails if the sender has '<' or '!' in
  1032. X     * the RFC822 comment fields -- leading "comment"
  1033. X     * or trailing (comment) -- but that isn't critical
  1034. X     */
  1035. X    if ((p2 = rindex(p, '!')) || (p2 = index(p, '<')))
  1036. X        p = p2 + 1;
  1037. X    } else
  1038. X    p = strcpy(from, "unknown"); /* just in case */
  1039. X    /* If the From field contains the user's login name, then the message is
  1040. X     * from the user -- attempt to give more useful information by telling
  1041. X     * to whom the message was sent.  This is not possible if the "to" header
  1042. X     * failed to get info (which is probably impossible).
  1043. X     */
  1044. X    if (!strncmp(p, login, strlen(login))) {
  1045. X    isauthor = TRUE;
  1046. X    (void) get_name_n_addr(to, name+4, addr+4);
  1047. X    if (addr[4])
  1048. X        (void) strncpy(addr, "TO: ", 4);
  1049. X    if (name[4]) {  /* check to see if a name got added */
  1050. X        (void) strncpy(name, "TO: ", 4);
  1051. X        Strncpy(from, name);
  1052. X    } else
  1053. X        Strncpy(from, addr);
  1054. X    } else
  1055. X    (void) get_name_n_addr(from, name, addr);
  1056. X
  1057. X    if (ison(glob_flags, DATE_RECV))
  1058. X    date_p = msg[cnt].m_date_recv;
  1059. X    else
  1060. X    date_p = msg[cnt].m_date_sent;
  1061. X    date_to_string(date_p, Yr, Mon, Day, Wkday, Tm, date);
  1062. X
  1063. X    /* and the subject */
  1064. X    if (p = header_field(cnt, "subject"))
  1065. X    Strncpy(subject, p);
  1066. X
  1067. X    /* now, construct a header out of a format string */
  1068. X    if (!hdr_format)
  1069. X    hdr_format = DEF_HDR_FMT;
  1070. X
  1071. X    (void) sprintf(buf, "%4.d ", cnt+1);
  1072. X    b = buf+5;
  1073. X    *b++ = ((cnt == current_msg && !iscurses)? '>': ' ');
  1074. X    *b++ = status[0], *b++ = status[1];
  1075. X    *b++ = ' ';
  1076. X    /* Count chars since beginning of buf. Initialize to 9 (strlen(buf) so far)
  1077. X     * This magic number is used in other places in msgs.c and mail.c
  1078. X     */
  1079. X    n = 9;
  1080. X    for (p = hdr_format; *p; p++)
  1081. X    if (*p == '\\')
  1082. X        switch (*++p) {
  1083. X        case 't':
  1084. X            while (n % 8)
  1085. X            n++, *b++ = ' ';
  1086. X        when 'n':
  1087. X            n = 1, *b++ = '\n';
  1088. X        otherwise: n++, *b++ = *p;
  1089. X        }
  1090. X    else if (*p == '%') {
  1091. X        char fmt[64];
  1092. X
  1093. X        p2 = fmt;
  1094. X        /* first check for string padding: %5n, %.4a, %10.5f, %-.3l etc. */
  1095. X        do_pad = pad = val = got_dot = 0;
  1096. X        *p2++ = '%';
  1097. X        if (p[1] != '-')
  1098. X        *p2++ = '-';
  1099. X        else
  1100. X        ++p;
  1101. X        while (isdigit(*++p) || !got_dot && *p == '.') {
  1102. X        if (*p == '.')
  1103. X            got_dot = TRUE, val = pad, pad = 0;
  1104. X        else
  1105. X            pad = pad * 10 + *p - '0';
  1106. X        *p2++ = *p;
  1107. X        }
  1108. X        if (!got_dot && isdigit(p[-1])) {
  1109. X        *p2 = 0; /* assure null termination */
  1110. X        val = atoi(fmt+1);
  1111. X        if (val < 0)
  1112. X            val = -val;
  1113. X        p2 += strlen(sprintf(p2, ".%d", val));
  1114. X        }
  1115. X        pad = min(pad, val);
  1116. X        *p2++ = 's', *p2 = 0;
  1117. X        switch (*p) {
  1118. X        case 'f': p2 = from, do_pad = TRUE;
  1119. X        when 'a':
  1120. X            if (!*(p2 = addr))
  1121. X            p2 = from;
  1122. X            do_pad = TRUE;
  1123. X        when 'n':
  1124. X            if (!*(p2 = name))
  1125. X            p2 = from, do_pad = TRUE;
  1126. X        when '%': p2 = "%";
  1127. X        when 't': p2 = to;
  1128. X        when 's': p2 = subject;
  1129. X        when 'l': p2 = sprintf(lines, "%d", msg[cnt].m_lines);
  1130. X        when 'c': p2 = sprintf(lines, "%ld", msg[cnt].m_size);
  1131. X        when 'i': p2 = header_field(cnt, "message-id");
  1132. X        /* date formatting chars */
  1133. X        when 'd': p2 = date; /* the full date */
  1134. X        when 'T': p2 = Tm;
  1135. X        when 'M': p2 = Mon;
  1136. X        when 'Y': p2 = Yr;
  1137. X        when 'y': p2 = Yr+2;
  1138. X        when 'N': p2 = Day;
  1139. X        when 'D': case 'W': p2 = Wkday;
  1140. X        otherwise: continue; /* unknown formatting char */
  1141. X        }
  1142. X        if (do_pad && pad && strlen(p2) > pad) {
  1143. X        char *old_p2 = p2, *p3;
  1144. X        /* if addr is too long, move pointer forward till the
  1145. X         * "important" part is readable only for ! paths/addresses.
  1146. X         */
  1147. X        while (p3 = index(p2, '!')) {
  1148. X            int tmp = strlen(p3+1); /* xenix has compiler problems */
  1149. X            p2 = p3+1;
  1150. X            if (tmp + isauthor*4 < pad) {
  1151. X            if (isauthor && (p2 -= 4) < old_p2)
  1152. X                p2 = old_p2;
  1153. X            break;
  1154. X            }
  1155. X        }
  1156. X        if (isauthor && p2 > old_p2+4 && !p3 && strlen(p2) + 4 > pad)
  1157. X            p2 -= 4;
  1158. X        if (old_p2 != p2 && isauthor)
  1159. X            (void) strncpy(p2, "TO: ", 4); /* doesn't null terminate */
  1160. X        }
  1161. X        len = strlen(sprintf(b, fmt, p2));
  1162. X        n += len, b += len;
  1163. X        /* Get around a bug in 5.5 IBM RT which pads with NULs not ' ' */
  1164. X        while (n && !*(b-1))
  1165. X        b--, n--;
  1166. X    } else
  1167. X        n++, *b++ = *p;
  1168. X    for (*b-- = 0; isspace(*b) && *b != '\n'; --b)
  1169. X    *b = 0;
  1170. X    return buf;
  1171. X}
  1172. X
  1173. X/*
  1174. X * Using message "n", build a list of recipients that you would mail to if
  1175. X * you were to reply to this message.  If "all" is true, then it will take
  1176. X * everyone from the To line in addition to the original sender.
  1177. X * route_addresses() is called from mail.c, not from here.  There are too many
  1178. X * other uses for reply_to to always require reconstruction of return paths.
  1179. X * Note that we do NOT deal with Cc paths here either.
  1180. X * Check to make sure that we in fact return a legit address (i.e. not blanks
  1181. X * or null). If such a case occurs, return login name.  Always pad end w/blank.
  1182. X */
  1183. Xchar *
  1184. Xreply_to(n, all, buf)
  1185. Xchar buf[];
  1186. X{
  1187. X    register char *p = NULL, *p2, *b = buf, *field;
  1188. X    char line[256], name[256], addr[256];
  1189. X
  1190. X    if (field = do_set(set_options, "reply_to_hdr")) {
  1191. X#ifndef MSG_SEPARATOR
  1192. X    if (!*field)
  1193. X        goto DoFrom; /* special case -- get the colon-less From line */
  1194. X#endif /* MSG_SEPARATOR */
  1195. X    field = lcase_strcpy(line, field);
  1196. X    while (*field) {
  1197. X        if (p2 = any(field, " \t,:"))
  1198. X        *p2 = 0;
  1199. X#ifndef MSG_SEPARATOR
  1200. X        if (!lcase_strncmp(field, "from_", -1))
  1201. X        goto DoFrom;
  1202. X#endif /* MSG_SEPARATOR */
  1203. X        if ((p = header_field(n, field)) || !p2)
  1204. X        break;
  1205. X        else {
  1206. X        field = p2+1;
  1207. X        while (isspace(*field) || *field == ':' || *field == ',')
  1208. X            field++;
  1209. X        }
  1210. X    }
  1211. X    if (!p)
  1212. X        print("Warning: message contains no `reply_to_hdr' headers.\n");
  1213. X    }
  1214. X    if (p || (!p && ((p = header_field(n, field = "reply-to")) ||
  1215. X        (p = header_field(n, field = "return-path")) ||
  1216. X        (p = header_field(n, field = "from")))))
  1217. X    skipspaces(0);
  1218. X    else if (!p) {
  1219. X#ifndef MSG_SEPARATOR
  1220. XDoFrom:
  1221. X    field = "from_";
  1222. X    /* if all else fails, then get the first token in "From" line */
  1223. X    if (p2 = msg_get(n, line, sizeof line))
  1224. X        p = index(p2, ' ');
  1225. X    else
  1226. X        return "";
  1227. X    skipspaces(1);
  1228. X    if (p2 = index(p, ' '))
  1229. X        *p2 = 0;
  1230. X    (void) unscramble_addr(p, line); /* p is safely recopied to line */
  1231. X    p = line;
  1232. X#else /* MSG_SEPARATOR */
  1233. X    wprint("Warning: unable to find who msg %d is from!\n", n+1);
  1234. X#endif /* MSG_SEPARATOR */
  1235. X    }
  1236. X    get_name_n_addr(p, name, addr);
  1237. X    if (!name[0] && (!lcase_strncmp(field, "return-path", -1) ||
  1238. X             !lcase_strncmp(field, "from_", -1))) {
  1239. X    /*
  1240. X     * Get the name of the author of the message we're replying to from the
  1241. X     * From: header since that header contains the author's name.  Only do
  1242. X     * this if the address was gotten from the return-path or from_ lines
  1243. X     * because this is the only way to guarantee that the return address
  1244. X     * matches the author's name.  Reply-To: may not be the same person!
  1245. X     * Check Resent-From: first since that's presumably more recent.
  1246. X     */
  1247. X    if ((p = header_field(n, "resent-from")) ||
  1248. X        (p = header_field(n, "from")))
  1249. X        get_name_n_addr(p, name, NULL);
  1250. X    if (!name[0] && (p = header_field(n, "name")))
  1251. X        (void) strcpy(name, p);
  1252. X    if (name[0]) {
  1253. X        if ((p = any(name, "(<,\"")) && (*p == ',' || *p == '<'))
  1254. X        *b++ = '"';
  1255. X        b += Strcpy(b, name);
  1256. X        if (p && (*p == ',' || *p == '<'))
  1257. X        *b++ = '"';
  1258. X        *b++ = ' ', *b++ = '<';
  1259. X    }
  1260. X    b += Strcpy(b, addr);
  1261. X    if (name[0])
  1262. X        *b++ = '>', *b = 0;
  1263. X    } else
  1264. X    b += Strcpy(buf, p);
  1265. X
  1266. X    /*
  1267. X     * if `all' is true, append everyone on the "To:" line.
  1268. X     * cc_to(), called separately, will catch the cc's
  1269. X     */
  1270. X    if (all &&
  1271. X    ((p = header_field(n, "resent-to")) || (p = header_field(n, "to")) ||
  1272. X     (p = header_field(n, "apparently-to"))) && *p) {
  1273. X    *b++ = ',', *b++ = ' ';
  1274. X    /* The assumption that HDRSIZ is correct is unwise, but I know it
  1275. X     * to be true for Mush.  Be forewarned if you call this routine.
  1276. X     */
  1277. X    p[HDRSIZ - (b - buf) - 2] = '\0'; /* prevent overflow */
  1278. X    b += Strcpy(b, p);
  1279. X    }
  1280. X    /* Also append the Resent-From address if there is one. */
  1281. X    if (all && (p = header_field(n, "resent-from")) && *p) {
  1282. X    *b++ = ',', *b++ = ' ';
  1283. X    /* Another trick to prevent overflow.   See warning above. */
  1284. X    (void) strncpy(b, p, HDRSIZ - (b - buf) - 2);
  1285. X    buf[HDRSIZ - 3] = 0;
  1286. X    }
  1287. X    fix_up_addr(buf);
  1288. X    take_me_off(buf);
  1289. X    for (p = buf; *p == ',' || isspace(*p); p++)
  1290. X    ;
  1291. X    if (!*p)
  1292. X    (void) strcpy(buf, login);
  1293. X    return buf;
  1294. X}
  1295. X
  1296. Xchar *
  1297. Xsubject_to(n, buf)
  1298. Xregister char *buf;
  1299. X{
  1300. X    register char *p;
  1301. X    buf[0] = 0; /* make sure it's already null terminated */
  1302. X    if (!(p = header_field(n, "subject")))
  1303. X    return NULL;
  1304. X    if (lcase_strncmp(p, "Re:", 3))
  1305. X    (void) strcpy(buf, "Re: ");
  1306. X    return strcat(buf, p);
  1307. X}
  1308. X
  1309. Xchar *
  1310. Xcc_to(n, buf)
  1311. Xregister char *buf;
  1312. X{
  1313. X    register char *p;
  1314. X    buf[0] = 0; /* make sure it's already null terminated */
  1315. X    if (!(p = header_field(n, "cc")))
  1316. X    return NULL;
  1317. X    fix_up_addr(p);
  1318. X    take_me_off(p);
  1319. X    return strcpy(buf, p);
  1320. X}
  1321. END_OF_FILE
  1322. if test 18781 -ne `wc -c <'hdrs.c'`; then
  1323.     echo shar: \"'hdrs.c'\" unpacked with wrong size!
  1324. fi
  1325. # end of 'hdrs.c'
  1326. fi
  1327. echo shar: End of archive 9 \(of 19\).
  1328. cp /dev/null ark9isdone
  1329. MISSING=""
  1330. for I in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 ; do
  1331.     if test ! -f ark${I}isdone ; then
  1332.     MISSING="${MISSING} ${I}"
  1333.     fi
  1334. done
  1335. if test "${MISSING}" = "" ; then
  1336.     echo You have unpacked all 19 archives.
  1337.     rm -f ark[1-9]isdone ark[1-9][0-9]isdone
  1338. else
  1339.     echo You still need to unpack the following archives:
  1340.     echo "        " ${MISSING}
  1341. fi
  1342. ##  End of shell archive.
  1343. exit 0
  1344.